﻿#region Copyright information
// 
// Copyright © 2005-2013 Yongkee Cho. All rights reserved.
// 
// This code is a part of the Biological Object Library and governed under the terms of the
// GNU Lesser General  Public License (LGPL) version 2.1 which accompanies this distribution.
// For more information on the LGPL, please visit http://bol.codeplex.com/license.
// 
// - Filename: VertexDictionaryGraph.cs
// - Author: Yongkee Cho
// - Email: yongkeecho@gmail.com
// - Date Created: 2013-01-24 4:34 PM
// - Last Modified: 2013-01-25 3:59 PM
// 
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using BOL.Linq;
using BOL.Linq.Matrix;

namespace BOL.Algorithms.Graph
{
    /// <summary>
    /// Represent a graph class.
    /// </summary>
    public class VertexDictionaryGraph<TVertex> : GraphBase, IVertexDictionaryGraph<TVertex>
    {
        #region Public properties

        /// <summary>Gets the graph vertices.</summary>
        public IDictionary<TVertex, IEnumerable<TVertex>> Dictionary { get; private set; }

        /// <summary>Returns the number of vertices in this graph.</summary>
        public override int Order { get { return Dictionary.Count; } }

        /// <summary>Returns the number of edges in this graph.</summary>
        public override int Size { get { return Dictionary.Sum(x => x.Value.Count()); } }

        /// <summary>Returns the maximum multiplicity of edges in a graph.</summary>
        public override int Multiplicity { get { return Math.Max(Dictionary.Select(x => x.Value.Count(y => y.Equals(x.Key))).Max(), 1); } }

        #endregion

        #region Constructors

        /// <summary>
        /// Overloaded. Initializes a new instance of the Graph class.
        /// </summary>
        public VertexDictionaryGraph(IEnumerable<IEdge<TVertex>> edges, bool isDirected = true)
            : base(isDirected)
        {
            if (edges == null)
                throw new ArgumentNullException("edges");

            var list = edges.ToList();
            var vertices = list.Select(x => x.Source).Concat(list.Select(y => y.Target)).Distinct().ToList();
            
            Dictionary = isDirected ? 
                vertices.ToDictionary(x => x, x => list.Where(y => y.Source.Equals(x)).Select(y => y.Target)) :
                vertices.ToDictionary(x => x, x => list.Where(y => y.Source.Equals(x)).Select(y => y.Target).Concat(list.Where(y => y.Target.Equals(x)).Select(y => y.Source)));
        }

        public VertexDictionaryGraph(IDictionary<TVertex, IEnumerable<TVertex>> dictionary, bool isDirected = true)
            : base(isDirected)
        {
            if (dictionary == null)
                throw new ArgumentNullException("dictionary");
            
            Dictionary = dictionary;
        }

        #endregion

        #region Public methods

        public override int[][] DegreeMatrix()
        {
            var degreeVector = Dictionary.Select(x => x.Value.Count()).ToArray();
            if (IsDirected)
            {
                var vertices = Dictionary.Select(x => x.Key).ToList();
                foreach (var target in vertices.SelectMany(source => Dictionary[source]))
                    degreeVector[vertices.IndexOf(target)]++;
            }

            return degreeVector.DiagonalMatrix();
        }

        public override bool[][] AdjacencyMatrix()
        {
            var vertices = Dictionary.Select(x => x.Key).ToList();
            var count = vertices.Count;
            var adjacencyMatrix = new bool[count].Select(x => new bool[count]).ToArray();
            for (var i = 0; i < count; i++)
            {
                foreach (var targetIndex in Dictionary[vertices[i]].Select(target => vertices.IndexOf(target)))
                {
                    adjacencyMatrix[i][targetIndex] = true;
                    if (IsDirected)
                        adjacencyMatrix[targetIndex][i] = true;
                }
            }

            return adjacencyMatrix;
        }

        public override int[][] IncidenceMatrix()
        {
            throw new NotSupportedException();
        }

        public override int[][] DistanceMatrix()
        {
            var vertices = Dictionary.Select(x => x.Key).ToList();
            var count = vertices.Count;
            var distanceMatrix = new int[count][];
            for (var i = 0; i < count; i++)
            {
                distanceMatrix[i] = new int[count];
                for (var j = i + 1; j < count; j++)
                {
                    //var distance = ShortestPath(vertices[i], vertices[j]);
                    var distance = 0;
                    distanceMatrix[i][j] = distance;
                    if (IsDirected)
                        distanceMatrix[j][i] = distance;
                }
            }

            return distanceMatrix;
        }

        #endregion

        #region ICloneable implementation

        /// <summary>
        /// Creates an exact copy of current graph. 
        /// </summary>
        protected override IGraph Clone()
        {
            return new VertexDictionaryGraph<TVertex>(Dictionary.ToDictionary(x => x.Key, x => x.Value), IsDirected);
        }

        #endregion

        #region IEquatable<VertexDictionaryGraph<TVertex>> implementation

        public bool Equals(VertexDictionaryGraph<TVertex> other)
        {
            return Dictionary.DictionaryEqual(other.Dictionary);
        }

        #endregion

        #region Object overriden

        public override int GetHashCode()
        {
            return Dictionary.Aggregate(1, (hashCode, current) => hashCode ^ current.Key.GetHashCode());
        }

        public override bool Equals(object other)
        {
            if (other == null)
                throw new ArgumentNullException();

            if (!(other is VertexDictionaryGraph<TVertex>))
                throw new InvalidCastException("The 'other' argument is not a VertexDictionaryGraph<TVertex> object.");

            return Equals(other as VertexDictionaryGraph<TVertex>);
        }

        #endregion
    }
}
